﻿using Clancy.Communication.Interfaces;
using Clancy.Data;
using Clancy.Interfaces;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;

namespace Clancy.Service.Base
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
    internal class ClancyCommunicationService : CommunicationServiceBase, IClancyCommunicationService
    {
        private readonly List<IClancyCommunicationCallbackService> m_CallbackChannels = new List<IClancyCommunicationCallbackService>();

        private readonly ISurveilanceLogic m_SurveilanceLogic;

        private readonly Dictionary<ISurveilanceStatus, List<IClancyCommunicationCallbackService>> m_SurveilanceSubscriptions =
            new Dictionary<ISurveilanceStatus, List<IClancyCommunicationCallbackService>>();

        private readonly IVideoSourceProvider m_VideoSourceProvider;
        private ISurveilanceStorage m_SurveilanceStorage;

        public ClancyCommunicationService(ISurveilanceLogic surveilanceLogic, ISurveilanceStorage surveilanceStorage, IVideoSourceProvider videoSourceProvider)
        {
            m_SurveilanceLogic = surveilanceLogic;
            m_SurveilanceStorage = surveilanceStorage;
            m_VideoSourceProvider = videoSourceProvider;

            AttachToCollection(m_SurveilanceStorage.SurveilanceObjects, SendSurveilanceAdded, SendSurveilanceRemoved);
        }

        public void AddNewSource(VideoSourceFilterInfo videoSourceFilterInfo)
        {
            m_SurveilanceLogic.AddNewSource(videoSourceFilterInfo);
        }

        public bool Connect()
        {
            m_CallbackChannels.Add(OperationContext.Current.GetCallbackChannel<IClancyCommunicationCallbackService>());
            return true;
        }

        public bool DeleteRecording(Guid selectedRecordingId)
        {
            throw new NotImplementedException();
        }

        public void DeleteVideoSource(SurveilanceObject surveilanceObject)
        {
            m_SurveilanceLogic.DeleteSurveilance(surveilanceObject);
        }

        public bool Disconnect()
        {
            IClancyCommunicationCallbackService currentChannel = OperationContext.Current.GetCallbackChannel<IClancyCommunicationCallbackService>();
            if (!m_CallbackChannels.Contains(currentChannel))
            {
                return false;
            }
            m_CallbackChannels.Remove(currentChannel);
            return true;
        }

        public override void Dispose()
        {
            m_SurveilanceSubscriptions.Clear();
            base.Dispose();
        }

        public VideoSourceFilterInfo[] GetAvailableSources()
        {
            return m_VideoSourceProvider.GetVideoSources().Cast<VideoSourceFilterInfo>().ToArray();
        }

        public SurveilanceObject[] GetSureveilanceObjects()
        {
            return m_SurveilanceStorage.SurveilanceObjects.Cast<SurveilanceObject>().ToArray();
        }

        public bool UpdateSurveilance(SurveilanceObject surveilanceObject)
        {
            ISurveilanceObject surveilanceToUpdate = m_SurveilanceStorage.SurveilanceObjects.FirstOrDefault(survObj => survObj.Id == surveilanceObject.Id);
            if (surveilanceToUpdate == null)
                return false;

            surveilanceToUpdate.AlarmDelayTime = surveilanceObject.AlarmDelayTime;
            surveilanceToUpdate.FlipX = surveilanceObject.FlipX;
            surveilanceToUpdate.FlipY = surveilanceObject.FlipY;
            surveilanceToUpdate.Framerate = surveilanceObject.Framerate;
            surveilanceToUpdate.MaxRecordDuration = surveilanceObject.MaxRecordDuration;
            surveilanceToUpdate.MotionDetectionEnabled = surveilanceObject.MotionDetectionEnabled;
            surveilanceToUpdate.MotionZones = surveilanceObject.MotionZones;
            surveilanceToUpdate.Name = surveilanceObject.Name;
            surveilanceToUpdate.PrefetchTime = surveilanceObject.PrefetchTime;
            surveilanceToUpdate.RecordBaseDirectory = surveilanceObject.RecordBaseDirectory;
            surveilanceToUpdate.RenderTimestamp = surveilanceObject.RenderTimestamp;
            surveilanceToUpdate.RotateAngle = surveilanceObject.RotateAngle;
            surveilanceToUpdate.Threshold = surveilanceObject.Threshold;

            return true;
        }

        public bool UpdateSurveilanceProperty(Guid surveilanceObjectId, string propertyName, string valueAsString)
        {
            ISurveilanceObject surveilanceToUpdate = m_SurveilanceStorage.SurveilanceObjects.FirstOrDefault(survObj => survObj.Id == surveilanceObjectId);
            if (surveilanceToUpdate == null)
                return false;

            PropertyInfo propertyToUpdate = surveilanceToUpdate.GetType().GetProperty(propertyName);
            object newPropertyValue;

            switch (propertyToUpdate.PropertyType.Name.ToLower())
            {
                case "string":
                    newPropertyValue = valueAsString;
                    break;

                case "double":
                    newPropertyValue = double.Parse(valueAsString);
                    break;

                case "int32":
                    newPropertyValue = int.Parse(valueAsString);
                    break;

                case "boolean":
                    newPropertyValue = bool.Parse(valueAsString);
                    break;

                case "timespan":
                    newPropertyValue = TimeSpan.Parse(valueAsString);
                    break;

                default:
                    return false;
            }

            propertyToUpdate.SetValue(surveilanceToUpdate, newPropertyValue);
            return true;
        }

        private void SendSurveilanceAdded(IList item)
        {
            SurveilanceObject[] addedSurveilances = item.Cast<SurveilanceObject>().ToArray();
            foreach (IClancyCommunicationCallbackService callbackChannel in m_CallbackChannels)
            {
                callbackChannel.ReturnSurveilancesAdded(addedSurveilances);
            }
        }

        private void SendSurveilanceRemoved(IList item)
        {
            SurveilanceObject[] removedSurveilances = item.Cast<SurveilanceObject>().ToArray();
            foreach (IClancyCommunicationCallbackService callbackChannel in m_CallbackChannels)
            {
                callbackChannel.ReturnSurveilancesRemoved(removedSurveilances);
            }
        }
    }
}